package com.kgcx.price.service;

import com.alibaba.fastjson.JSONObject;
import com.kgcx.common.client.coupon.CouponClient;
import com.kgcx.common.client.coupon.pojo.Coupon;
import com.kgcx.common.client.order.OrderClient;
import com.kgcx.common.client.order.pojo.Order;
import com.kgcx.common.rabbitmq.bean.Message;
import com.kgcx.common.rabbitmq.consts.ExchangeConst;
import com.kgcx.common.rabbitmq.consts.RoutingKeyConst;
import com.kgcx.common.utils.CollectionUtils;
import com.kgcx.common.utils.GaodeApi;
import com.kgcx.core.exception.BusinessException;
import com.kgcx.price.domain.MileageRecord;
import com.kgcx.price.domain.PriceActualPrice;
import com.kgcx.price.domain.PriceChangePriceDetail;
import com.kgcx.price.domain.TPriceCaculationRule;
import com.kgcx.price.mapper.MileageRecordMapper;
import com.kgcx.price.mapper.PriceActualPriceMapper;
import com.kgcx.price.mapper.PriceChangePriceDetailMapper;
import com.kgcx.price.mapper.TPriceCaculationRuleMapper;
import com.kgcx.price.utils.DateUtil;
import com.kgcx.price.utils.SystemVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * CaculationRule 实际收费计算类 Service层
 *
 * @author:zhoumeile
 * @date:2017/11/07
 */
@Service
public class PriceActualPriceService {
    @Autowired
    private PriceActualPriceMapper priceActualPriceMapper;

    @Autowired
    private TPriceCaculationRuleMapper tPriceCaculationRuleMapper;

    @Autowired
    private MileageRecordMapper mileageRecordMapper;

    @Autowired
    private GaodeApi gaodeApi;

    @Autowired
    private CouponClient couponClient;

    @Autowired
    private OrderClient orderClient;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PriceChangePriceDetailMapper priceChangePriceDetailMapper;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * caculatePrice 计算费用方法
     *
     * @param priceActualPrice 实际收费价信息类
     * @return PriceActualPrice
     */
    @Transactional
    public PriceActualPrice caculatePrice(PriceActualPrice priceActualPrice) {
        TPriceCaculationRule tPriceCaculationRuleForDb = new TPriceCaculationRule();

        if (priceActualPrice.getCarType() != null) {
            tPriceCaculationRuleForDb.setCarType(priceActualPrice.getCarType());
        } else {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "用车类型为空！");
        }

        if (priceActualPrice.getCityId() != null) {
            tPriceCaculationRuleForDb.setCityId(priceActualPrice.getCityId());
        } else {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "城市为空！");
        }

        if (priceActualPrice.getOrderId() == null) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "订单号为空！");
        }

        if (priceActualPrice.getDriverId() == null) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "司机为空！");
        }

        if (priceActualPrice.getCustomerId() == null) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "乘客为空！");
        }


        /** 根据城市、用车类型取得唯一一条报价规则信息 */
        TPriceCaculationRule tPriceCaculationRule = tPriceCaculationRuleMapper.getByCityIdCarType(tPriceCaculationRuleForDb);

        /** 如果查询到报价规则相关信息则进行价格预估，否则抛错 */
        if (tPriceCaculationRule != null) {
            /** 订单预估总价 */
            Long totalPrice;
            /** 起步基础价 */
            BigDecimal startFee = tPriceCaculationRule.getStartFee();
            /** 时长单价 */
            BigDecimal unitpriceperminute = tPriceCaculationRule.getUnitpriceperminute();
            /** 公里单价 */
            BigDecimal unitpricepermile = tPriceCaculationRule.getUnitpricepermile();
            /** 远途费单价 */
            BigDecimal travelFee = tPriceCaculationRule.getFarAwayPrice();
            /** 出城费单价 */
            BigDecimal outCityPerFee = tPriceCaculationRule.getOutTownPrice();
            /** 夜间服务费单价 */
            BigDecimal evePerFee = tPriceCaculationRule.getNightPrice();
            /** 总时长价 */
            BigDecimal minuteFee;
            /** 总公里价 */
            BigDecimal mileFee;
            /** 远途总价 */
            BigDecimal travelTotleFee = new BigDecimal(0);
            /** 出城费总价 */
            BigDecimal outCityFee = new BigDecimal(0);
            /** 夜间服务费总价 */
            BigDecimal eveFee = new BigDecimal(0);
            /** 抹零数 */
            Long zero;
            /** 订单预估价(抹零后) */
            Long totalMoney;

            priceActualPrice.setPriceId(tPriceCaculationRule.getKid());

            /** 存数据库，都存基本单位 分 */
            startFee = startFee.multiply(new BigDecimal(100));
            priceActualPrice.setStartFee(startFee.longValue());
            /** 是否套餐0否1是 */
            priceActualPrice.setType(0);
            /** 计价规则类型 1套餐 2普通 3一口价 */
            priceActualPrice.setCalType(2);

            /** 查询里程表相关信息，取得实际行驶里程 */
            MileageRecord mileageRecordDB = new MileageRecord();
            mileageRecordDB.setOrderId(priceActualPrice.getOrderId());

            MileageRecord mileageRecord = mileageRecordMapper.selectOneActive(mileageRecordDB);
            BigDecimal distance;
            BigDecimal duration;

            if (mileageRecord != null) {
                Long distanceDB = mileageRecord.getEndMileage() - mileageRecord.getStartMileage();
                priceActualPrice.setMileage(distanceDB);

                distance = (new BigDecimal(distanceDB).divide(new BigDecimal(1000))).setScale(1, BigDecimal.ROUND_DOWN);
            } else {
                throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "未查询到里程相关信息！");
            }

            /** 调用订单相关信息，取得订单开始时间、结束时间 */
            Order order = orderClient.getByOrderNo(priceActualPrice.getOrderId());
            if (order != null) {
                if (order.getDepTime() != null && order.getDestTime() != null) {
                    Long startTime = order.getDepTime().getTime();
                    Long endTime = order.getDestTime().getTime();

                    duration = new BigDecimal(endTime).subtract(new BigDecimal(startTime)).divide(new BigDecimal(1000)).abs();
                    priceActualPrice.setTime(duration.intValue());
                } else {
                    throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "订单开始时间、结束时间为空！");
                }
            } else {
                throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "未查询到订单相关信息！");
            }

            /** 计算总时长费,秒为单位 总用时 * 每分钟单价 */
            minuteFee = unitpriceperminute.multiply(new BigDecimal((duration.longValue() / 60))).multiply(new BigDecimal(100));
            /** 计算公里价，米为单位 总里程 * 每公里单价 */
            mileFee = unitpricepermile.multiply(distance).multiply(new BigDecimal(100));

            priceActualPrice.setMileageMoney(mileFee.longValue());
            priceActualPrice.setTimeFee(minuteFee.longValue());

            /** 计算是否有远途费,单位为米，限制为15公里 */
            if (distance.compareTo(SystemVariable.TRAVEL_MILE) == 1) {
                priceActualPrice.setTravelFlag(1);
                /** 计算远途费 远途费 - 15公里 * 远途费单价 */
                travelTotleFee = distance.subtract(SystemVariable.TRAVEL_MILE).multiply(travelFee).multiply(new BigDecimal(100));

                priceActualPrice.setKmLong(distance.subtract(SystemVariable.TRAVEL_MILE).multiply(new BigDecimal(1000)).longValue());
            } else {
                priceActualPrice.setTravelFlag(0);
            }

            priceActualPrice.setTravelFee(travelTotleFee.longValue());

            /** 调用高德逆地理编码接口，根据返回cityCode判断是否出城 */
            List<Map<String, String>> gaodeGeocode = gaodeApi.geocode(priceActualPrice.getOrigin() + "|" + priceActualPrice.getDestination());

            /** 根据高德逆地理编码返回 citycode 判断是否相等 */
            if (gaodeGeocode.get(0).get("citycode").equals(gaodeGeocode.get(1).get("citycode"))) {
                priceActualPrice.setOutCityFlag(0);
                priceActualPrice.setCityFee(outCityFee.longValue());
            } else {
                priceActualPrice.setOutCityFlag(1);
                /** 计算出城费 总里程 * 出城费单价 */
                outCityFee = outCityPerFee.multiply(distance).multiply(new BigDecimal(100));
                priceActualPrice.setCityFee(outCityFee.longValue());
            }

            /** 计算夜间行车费，计算逻辑：以订单开始时间为依据，若开始时间在时间区间内则收取，否则不收 */
            Date strToDate = order.getDepTime();

            /** 如果有夜间服务费，则计算  总里程 * 夜间服务费单价 */
            if (DateUtil.belongCalendar(tPriceCaculationRule.getNightPriceStartTime(), tPriceCaculationRule.getNightPriceEndTime(), strToDate)) {
                eveFee = evePerFee.multiply(distance).multiply(new BigDecimal(100));
                priceActualPrice.setEveFee(eveFee.longValue());
            }

            Long conponFee = 0L;

            /** 调用优惠券查询接口，查询用户相关联优惠券信息 */
            Long trigger = startFee.longValue() + minuteFee.longValue() + mileFee.longValue() + travelTotleFee.longValue() + eveFee.longValue() + outCityFee.longValue();


            List<Coupon> list = couponClient.queryUsableCouponByOrderNo(order.getOrderNo(), trigger);

            if (CollectionUtils.isNotEmpty(list)) {
                Coupon coupon = list.get(0);
                conponFee = coupon.getMoney();
                priceActualPrice.setCouponId(coupon.getKid());
                priceActualPrice.setCouponFee(conponFee);
            } else {
                priceActualPrice.setCouponId(null);
                priceActualPrice.setCouponFee(null);
            }


            /** 总价汇总（未抹零）计算 总价 = 起步价 + 时长总价 + 公里总价 + 远途总价 + 夜间服务总价 + 出城总价 */
            totalPrice = startFee.longValue() + minuteFee.longValue() + mileFee.longValue() + travelTotleFee.longValue() + eveFee.longValue() + outCityFee.longValue();

            /** 减优惠券之前的总计预估价 */
            priceActualPrice.setTotalMoney(totalPrice);

            /** 总计预估价，未抹零 */
            totalPrice = totalPrice - conponFee;

            if (totalPrice < 0) {
                totalPrice = 0L;
            }

            priceActualPrice.setTotals(totalPrice);

            caculateFinalTotalFee(priceActualPrice);

            priceActualPrice.setStatus(101);

            /** 费用信息入库 */
            PriceActualPrice priceActualPriceDB = new PriceActualPrice();
            priceActualPriceDB.setOrderId(priceActualPrice.getOrderId());

            if (priceActualPriceMapper.selectOneActive(priceActualPriceDB) == null) {
                priceActualPriceMapper.insertSelective(priceActualPrice);
                /** 向订单发起mq消息，通知更新实际收费价格 */
                try {
                    addPriceActualPriceMq(priceActualPrice);
                } catch (Exception e) {
                }
            }
            return priceActualPrice;
        } else {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "未查询到有效报价基础规则！");
        }

    }


    /**
     * updateActualPrice 添加其他费用信息
     *
     * @param priceActualPrice 实际收费价信息类
     * @return PriceActualPrice
     */
    public PriceActualPrice addOtherActualPrice(PriceActualPrice priceActualPrice) {
        /** 费用总价,减优惠卷之前（未抹零) */
        BigDecimal totalMoney;
        /** 费用总价（未抹零) */
        BigDecimal totals;
        /** 抹零数 */
        Long zero;
        /** 订单总价价(抹零后) */

        if (StringUtils.isEmpty(priceActualPrice.getOrderId())) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "根据订单信息未查询到费用信息！");
        } else {
            try {
                PriceActualPrice priceActualPriceResult = this.getPriceActualPriceByOrderId(priceActualPrice.getOrderId());

                if (priceActualPriceResult == null) {
                    throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "未查找到有效实际价格信息！");
                }

                totals = new BigDecimal(priceActualPriceResult.getTotals());
                totalMoney = new BigDecimal(priceActualPriceResult.getTotalMoney());
                /** 停车费 */
                if (priceActualPrice.getParkingFee() != null) {
                    totals = totals.add(new BigDecimal(priceActualPrice.getParkingFee() * 100));
                    totalMoney = totalMoney.add(new BigDecimal(priceActualPrice.getParkingFee() * 100));

                    if (priceActualPriceResult.getParkingFee() != null) {
                        totals = totals.subtract(new BigDecimal(priceActualPriceResult.getParkingFee()));
                        totalMoney = totalMoney.subtract(new BigDecimal(priceActualPriceResult.getParkingFee()));
                    }
                    priceActualPriceResult.setParkingFee(priceActualPrice.getParkingFee() * 100);
                }

                /** 路桥费 */
                if (priceActualPrice.getServeiceCharge() != null) {
                    totals = totals.add(new BigDecimal(priceActualPrice.getServeiceCharge() * 100));
                    totalMoney = totalMoney.add(new BigDecimal(priceActualPrice.getServeiceCharge() * 100));

                    if (priceActualPriceResult.getServeiceCharge() != null) {
                        totals = totals.subtract(new BigDecimal(priceActualPriceResult.getServeiceCharge()));
                        totalMoney = totalMoney.subtract(new BigDecimal(priceActualPriceResult.getServeiceCharge()));
                    }

                    priceActualPriceResult.setServeiceCharge(priceActualPrice.getServeiceCharge() * 100);
                }

                /** 其他费用 */
                if (priceActualPrice.getOtherFee() != null) {
                    totals = totals.add(new BigDecimal(priceActualPrice.getOtherFee() * 100));
                    totalMoney = totalMoney.add(new BigDecimal(priceActualPrice.getOtherFee() * 100));

                    if (priceActualPriceResult.getOtherFee() != null) {
                        totals = totals.subtract(new BigDecimal(priceActualPriceResult.getOtherFee()));
                        totalMoney = totalMoney.subtract(new BigDecimal(priceActualPriceResult.getOtherFee()));
                    }

                    priceActualPriceResult.setOtherFee(priceActualPrice.getOtherFee() * 100);
                }

                /** 减优惠券之前的总计预估价(未抹零) */
                priceActualPriceResult.setTotalMoney(totalMoney.longValue());
                /** 总计预估价，未抹零 */
                priceActualPriceResult.setTotals(totals.longValue());
                /** 优惠券金额 */
                //caculateCoupon(priceActualPriceResult);
                /** 计算抹零后总价 */
                caculateFinalTotalFee(priceActualPriceResult);


                updatePriceActualPrice(priceActualPriceResult);
                return priceActualPriceResult;
            } catch (Exception e) {
                logger.error("更新收费明细失败:", e);
                throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "更新收费明细失败:" + e.getMessage());
            }
        }
    }

    private void caculateFinalTotalFee(PriceActualPrice priceActualPrice) {
        /** 总价抹零 */
        String sTotalPrice = String.valueOf(priceActualPrice.getTotals());
        Long zero = 0L;
        if (priceActualPrice.getTotals() > 0 && sTotalPrice.length() >= 2) {
            zero = Long.valueOf(sTotalPrice.substring(sTotalPrice.length() - 2, sTotalPrice.length()));
        }
        /** 抹零后总价 */
        Long totalFee = priceActualPrice.getTotals() - zero;

        priceActualPrice.setZero(zero);
        priceActualPrice.setTotalFee(totalFee);
    }

    public void updatePriceActualPrice(PriceActualPrice priceActualPriceResult) {
        if (priceActualPriceMapper.updateByPrimaryKeySelective(priceActualPriceResult) > 0) {
            /** 向订单发起mq消息，通知更新实际收费价格 */
            try {
                addPriceActualPriceMq(priceActualPriceResult);
            } catch (Exception e) {
            }

        } else {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "更新收费明细失败！");
        }
    }

    public void changeCoupon(String orderId, Long couponId) {
        PriceActualPrice priceActualPriceResult = getPriceActualPriceByOrderId(orderId);
        Long oldCouponId = priceActualPriceResult.getCouponId();
        if (null != oldCouponId) {
            couponClient.updateCouponState(oldCouponId, Coupon.STATE_UNUSED);
        }
        priceActualPriceResult.setCouponId(couponId);
        caculateCoupon(priceActualPriceResult);
        caculateFinalTotalFee(priceActualPriceResult);
        updatePriceActualPrice(priceActualPriceResult);
        couponClient.updateCouponState(couponId, Coupon.STATE_USING);

    }

    public void caculateCoupon(PriceActualPrice priceActualPrice) {

        BigDecimal totals = new BigDecimal(priceActualPrice.getTotalMoney());
        /** 优惠券金额 */
        if (priceActualPrice.getCouponId() != null) {
            if (priceActualPrice.getCouponId() == 0) {
                if (priceActualPrice.getCouponFee() != null) {
                    totals = totals.add(new BigDecimal(priceActualPrice.getCouponFee()));
                    priceActualPrice.setCouponFee(0L);
                    priceActualPrice.setCouponId(0L);
                }
            } else {
                Coupon coupon = couponClient.getCouponInfo(priceActualPrice.getCouponId());

                if (coupon != null) {
                    //如果原来有优惠卷，则还原抵扣前的价格
//                    if (priceActualPrice.getCouponFee() != null) {
//                        totals = totals.add(new BigDecimal(priceActualPrice.getCouponFee()));
//                    }

                    Long couponFee = coupon.getMoney();
                    priceActualPrice.setCouponId(coupon.getKid());
                    priceActualPrice.setCouponFee(coupon.getMoney());

                    totals = totals.subtract(new BigDecimal(couponFee));
                    //不可优惠卷减免的价格，停车费、服务费、其他费用
                    Long noSub = 0L;

                    if (null != priceActualPrice.getParkingFee()) {
                        noSub = noSub + priceActualPrice.getParkingFee().longValue();
                    }
                    if (null != priceActualPrice.getServeiceCharge()) {
                        noSub = noSub + priceActualPrice.getServeiceCharge().longValue();
                    }
                    if (null != priceActualPrice.getOtherFee()) {
                        noSub = noSub + priceActualPrice.getOtherFee().longValue();
                    }
                    if (totals.longValue() < noSub) {
                        totals = new BigDecimal(noSub);
                    }

                    if (totals.longValue() < 0) {
                        totals = new BigDecimal(0);
                    }
                }
            }
        } else {
            priceActualPrice.setCouponFee(0L);
        }
        priceActualPrice.setTotals(totals.longValue());
    }

    /**
     * getPriceActualPriceByOrderId 根据订单号查询实际计价信息
     *
     * @param orderId 订单编号
     * @return ReturnData
     */
    public PriceActualPrice getPriceActualPriceByOrderId(String orderId) {

        PriceActualPrice priceActualPrice = new PriceActualPrice();
        priceActualPrice.setOrderId(orderId);

        return priceActualPriceMapper.selectOneActive(priceActualPrice);
    }

    /**
     * changePrice 调价接口
     *
     * @param price 调整金额
     * @return ReturnData
     */
    public void changePrice(String price, String orderId, Integer adjustType) {
        if (StringUtils.isEmpty(orderId)) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "订单号为空");
        }

        if (StringUtils.isEmpty(price)) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "调价金额为空");
        }


        PriceActualPrice priceActualPriceDB = new PriceActualPrice();
        priceActualPriceDB.setOrderId(orderId);

        PriceActualPrice priceActualPriceResult = priceActualPriceMapper.selectOneActive(priceActualPriceDB);

        if (priceActualPriceResult == null) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "根据订单号未查到实际计价信息！");
        }

        if (priceActualPriceResult.getTotalMoney() == (Long.valueOf(price) * 100)) {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "调价金额与订单实际金额一致，无需调价！");
        }

        if (priceActualPriceResult.getHisFee() == null) {
            priceActualPriceResult.setHisFee(priceActualPriceResult.getTotalMoney());
        }

        /** 调价明细数据 */
        PriceChangePriceDetail priceChangePriceDetail = new PriceChangePriceDetail();

        priceChangePriceDetail.setOrderId(orderId);
        priceChangePriceDetail.setPriceBefore(new BigDecimal(priceActualPriceResult.getTotalMoney()));
        priceChangePriceDetail.setPriceAfter(new BigDecimal(Long.valueOf(price) * 100));
        priceChangePriceDetail.setAdjustType(adjustType.byteValue());

        priceActualPriceResult.setAdjustType(adjustType);
        priceActualPriceResult.setTotals(Long.valueOf(price) * 100);
        priceActualPriceResult.setTotalMoney(Long.valueOf(price) * 100);
        priceActualPriceResult.setTotalFee(Long.valueOf(price) * 100);

        if (priceActualPriceMapper.updateByPrimaryKeySelective(priceActualPriceResult) > 0) {
            /** 向订单发起mq消息，通知更新实际收费价格 */
            try {
                addChangePriceDetail(priceChangePriceDetail);
                addPriceActualPriceMq(priceActualPriceResult);
            } catch (Exception e) {
            }
        } else {
            throw new BusinessException(SystemVariable.ERROR_RETURN_CODE, "调价失败！");
        }

    }


    private void addPriceActualPriceMq(PriceActualPrice priceActualPrice) {
        logger.info(String.format("新增实际计价成功的MQ消息，订单id[%s]。", priceActualPrice.getOrderId()));
        Message msg = Message.buildMessage("priceActualPriceMQ", Message.FLAG_ADD, priceActualPrice);
        rabbitTemplate.convertAndSend(ExchangeConst.PRICE, RoutingKeyConst.PRICE_NOTIFY_ORDER_UPDATE_PRICE, JSONObject.toJSONString(msg));
    }

    private void addChangePriceDetail(PriceChangePriceDetail priceChangePriceDetail) {
        priceChangePriceDetailMapper.insertSelective(priceChangePriceDetail);
    }

    public Long getTotalPriceByOrderId(List<String> orderIds) {
        PriceActualPrice totalPriceByOrderId = priceActualPriceMapper.getTotalPriceByOrderId(orderIds);

        if (null != totalPriceByOrderId) {
            return totalPriceByOrderId.getTotals();
        }
        return 0L;
    }


}
